package rwmutex

import (
	"fmt"
	"sync"
	"time"
)

/*
   RWMutex(读写锁)
   RWMutex是读/写互斥锁。锁可以由任意数量的读取器或单个编写器持有。RWMutex的零值是未锁定的mutex。

   当有一个 goroutine 获得写锁定，其它无论是读锁定还是写锁定都将阻塞直到写解锁；
   当有一个 goroutine 获得读锁定，其它读锁定仍然可以继续；
   当有一个或任意多个读锁定，写锁定将等待所有读锁定解锁之后才能够进行写锁定。
   所以说这里的读锁定（RLock）目的其实是告诉写锁定：有很多人正在读取数据，你给我站一边去，等它们读（读解锁）完你再来写（写锁定）。

    同时只能有一个 goroutine 能够获得写锁定。
	同时可以有任意多个 gorouinte 获得读锁定。
    同时只能存在写锁定或读锁定（读和写互斥）。

    RWMutex这个读写锁，该锁可以加多个读锁或者一个写锁，其经常用于读次数远远多于写次数的场景。

    RLock()方法
    读锁，当有写锁时，无法加载读锁，当只有读锁或者没有锁时，可以加载读锁，读锁可以加载多个，所以适用于“读多写少”的场景。

    RUnlock()方法
    读锁解锁，RUnlock 撤销单次RLock调用，它对于其它同时存在的读取器则没有效果。若rw并没有为读取而锁定，调用RUnlock就会引发一个运行时错误。

    Lock()方法：
    写锁，如果在添加写锁之前已经有其他的读锁和写锁，则Lock就会阻塞直到该锁可用，为确保该锁最终可用，
    已阻塞的Lock调用会从获得的锁中排除新的读取锁，即写锁权限高于读锁，有写锁时优先进行写锁定。

    Unlock()方法
    写锁解锁，如果没有进行写锁定，则就会引起一个运行时错误。




    读锁不能阻塞读锁。
	读锁需要阻塞写锁，直到所有读锁都释放。
    写锁需要阻塞读锁，直到所有写锁都释放。
    写锁需要阻塞写锁。

*/

var rwMutex *sync.RWMutex
var wg *sync.WaitGroup

func main() {

	rwMutex = new(sync.RWMutex)
	wg = new(sync.WaitGroup)

	wg.Add(3)

	go writeData(1)
	go readData(2)
	go writeData(3)

	wg.Wait()

}

func writeData(i int) {
	defer wg.Done()

	fmt.Println(i, "开始写：write start....")
	//写操作上锁
	rwMutex.Lock()
	fmt.Println(i, "正在写：writing....")
	time.Sleep(3 * time.Second)
	//写操作解锁
	rwMutex.Unlock()

	fmt.Println(i, "写结束：write over....")
}

func readData(i int) {
	defer wg.Done()

	fmt.Println(i, "开始读：read start....")
	//读操作上锁
	rwMutex.RLock()
	fmt.Println(i, "正在读取数据：reading....")
	time.Sleep(3 * time.Second)
	//读操作解锁
	rwMutex.RUnlock()

	fmt.Println(i, "读结束：read over....")
}
